﻿using System.Linq.Expressions;
using PropertyExpression.Common;

namespace PropertyExpression.RelationalDataInstance
{
    public static class DataInstanceSchemaItem
    {
        public static IDataInstanceSchemaItem<TEntity> Reference<TEntity, TItem>(
            this IDataInstanceSchemaItem<TEntity> it,
            Func<IEdgeInferencer<TEntity>, Edge<TEntity, TItem>> inferencerFunc,
            Action<IDataInstanceSchemaItem<TItem>> action)
        {
            return inferencerFunc(new EdgeInferencer<TEntity>(it))(action);
        }

        private class EdgeInferencer<TEntity> : IEdgeInferencer<TEntity>
        {
            private readonly IDataInstanceSchemaItem<TEntity> dataInstanceSchemaItem;

            public EdgeInferencer(IDataInstanceSchemaItem<TEntity> dataInstanceSchemaItem)
            {
                this.dataInstanceSchemaItem = dataInstanceSchemaItem;
            }

            public Edge<TEntity, TItem> _<TItem>(
                IPropertyExpressionProxy<TEntity, IManyReferenceEdge<TItem>> propertyExpressionProxy)
            {
                return action => dataInstanceSchemaItem.Reference(propertyExpressionProxy, action);
            }

            public Edge<TEntity, TProperty> _<TProperty>(
                IPropertyExpressionProxy<TEntity, TProperty> propertyExpressionProxy)
            {
                return action => dataInstanceSchemaItem.Reference(propertyExpressionProxy, action);
            }
        }

        public static IDataInstanceSchemaItem<TEntity> Reference<TEntity, TProperty>(
            this IDataInstanceSchemaItem<TEntity> it,
            Expression<Func<TEntity, TProperty>> propertyExpression)
        {
            return it.Reference(i => i._(propertyExpression), delegate { });
        }

        public static IDataInstanceSchemaItem<TEntity> Reference<TEntity, TItem>(
            this IDataInstanceSchemaItem<TEntity> it,
            Expression<Func<TEntity, IManyReferenceEdge<TItem>>> propertyExpression)
        {
            return it.Reference(i => i._(propertyExpression), delegate { });
        }

        public static IDataInstanceSchemaItem<TEntity> Field<TEntity, TProperty>(
            this IDataInstanceSchemaItem<TEntity> it,
            Expression<Func<TEntity, TProperty>> propertyExpression)
        {
            return it.Field(propertyExpression.ToPropertyExpressionProxy());
        }
    }

    public interface IEdgeInferencer<out TEntity>
    {
        Edge<TEntity, TItem> _<TItem>(IPropertyExpressionProxy<TEntity, IManyReferenceEdge<TItem>> propertyExpressionProxy);

        Edge<TEntity, TProperty> _<TProperty>(IPropertyExpressionProxy<TEntity, TProperty> propertyExpressionProxy);
    }

    public static class EdgeInferencer
    {
        public static Edge<TEntity, TItem> _<TEntity, TItem>(
            this IEdgeInferencer<TEntity> it,
            Expression<Func<TEntity, IManyReferenceEdge<TItem>>> propertyExpression)
        {
            return it._(propertyExpression.ToPropertyExpressionProxy());
        }

        public static Edge<TEntity, TProperty> _<TEntity, TProperty>(
            this IEdgeInferencer<TEntity> it,
            Expression<Func<TEntity, TProperty>> propertyExpression)
        {
            return it._(propertyExpression.ToPropertyExpressionProxy());
        }
    }

    public delegate IDataInstanceSchemaItem<TEntity> Edge<out TEntity, out TProperty>(Action<IDataInstanceSchemaItem<TProperty>> action);
}